4 from __future__
import division
17 '''Devuelve un valor de angulo equivalente en [0, 2*pi).
22 >>> mod2pi(4 * math.pi)
29 while x
>= 2 * math
.pi
:
35 '''Devuelve un valor de angulo equivalente en [0, 360).
57 def __init__(self
, x
, y
):
61 def __add__(self
, otro
):
62 return Vector(self
.x
+ otro
.x
, self
.y
+ otro
.y
)
64 def __sub__(self
, otro
):
65 return Vector(self
.x
- otro
.x
, self
.y
- otro
.y
)
68 return Vector(self
.x
* f
, self
.y
* f
)
70 def __rmul__(self
, f
):
74 return Vector(self
.x
/ f
, self
.y
/ f
)
77 return Vector(self
.x
*-1 , self
.y
* -1)
79 def __eq__(self
, otro
):
80 return self
.x
== otro
.x
and self
.y
== otro
.y
83 return '''<Vector %s,%s>''' % (self
.x
, self
.y
)
90 return hash((self
.x
,self
.y
))
93 '''Computa el producto escalar de 2 vectores.
95 >>> Vector(1,0).dotP(Vector(0,1))
97 >>> Vector(1,1).dotP(Vector(1,1))
101 return self
.x
* otro
.x
+ self
.y
* otro
.y
103 def boundNorm(self
, norma
):
104 '''Devuelve un vector idéntico a este pero con la norma
105 acotada al rango [0, norma]
107 >>> Vector(2,0).boundNorm(1)
109 >>> Vector(1,1).boundNorm(4)
111 >>> Vector(2,2).boundNorm(math.sqrt(2))
116 norma_efectiva
= min(norma
, self
.norma())
117 factor
= norma_efectiva
/ self
.norma()
121 '''Devuelve un vector idéntico a este con la coordenada
122 Y invertida (útil para dibujar en la pantalla, que tiene el
123 sistema de coordenadas con esta particularidad).
125 return Vector(self
.x
, -self
.y
)
128 '''Computa la norma cartesiana del vector.
130 >>> Vector(1,1).norma()
132 >>> Vector(1,0).norma()
134 >>> Vector(0,1).norma()
137 return math
.sqrt(self
.x
**2 + self
.y
**2)
140 '''Computa el ángulo polar del vector (en radianes desde la recta y=0).
142 >>> Vector(1,0).angulo()
144 >>> Vector(0,1).angulo()
146 >>> Vector(0,-1).angulo()
149 if self
.x
== 0 and self
.y
== 0:
152 return math
.asin(self
.y
/self
.norma())
154 return -math
.asin(self
.y
/self
.norma()) + math
.pi
157 '''Computa el ángulo polar del vector (en grados desde la recta y=0).
159 >>> Vector(1,0).grados()
161 >>> Vector(0,1).grados()
163 >>> Vector(0,-1).grados()
166 return math
.degrees(self
.angulo())
169 '''Devuelve el viento correspondiente a este vector.
171 >>> viento = Vector(10,10).viento()
173 <Viento 225.0º de 14.1kt>
174 >>> Vector(1, 0).viento()
175 <Viento 270.0º de 1.0kt>
176 >>> Vector(-10, 0).viento()
177 <Viento 90.0º de 10.0kt>
179 return Viento(mod360(270 - self
.grados()), self
.norma())
187 def __init__(self
, direccion
, intensidad
):
188 '''Construye un viento soplando desde la direccion indicada,
189 con la intensidad indicada.
191 >>> viento = Viento(0, 15)
195 -2.7553642961003488e-15
200 assert 0 <= intensidad
201 direccion
= mod360(direccion
)
202 self
.direccion
= direccion
203 self
.intensidad
= intensidad
205 # Precalculo algunas cosas
206 self
.angulo
= mod2pi(-math
.radians(direccion
) - math
.pi
/2)
207 self
.x
= math
.cos(self
.angulo
) * intensidad
208 self
.y
= math
.sin(self
.angulo
) * intensidad
211 '''Devuelve el vector asociado a este viento.
213 >>> vector = Viento(90,10).vector()
217 1.2246063538223773e-15
219 <Viento 90.0º de 10.0kt>
221 return Vector(self
.x
, self
.y
)
223 def anguloIncidencia(self
, vector_navegacion
):
224 '''Devuelve el ángulo de incidencia del viento sobre un
225 barco que navega sobre vector_navegacion.
227 >>> Viento(0, 10).anguloIncidencia(Vector(-1,1))
229 >>> Viento(0, 10).anguloIncidencia(Vector(1,1))
232 return mod2pi(vector_navegacion
.angulo() - (-self
.vector()).angulo())
235 return "<Viento %.1fº de %.1fkt>" % (self
.direccion
, self
.intensidad
)
243 def intersect_lines(l1
, l2
):
250 den
= (p4
.y
-p3
.y
)*(p2
.x
-p1
.x
) - (p4
.x
-p3
.x
)*(p2
.y
-p1
.y
)
252 ua_num
= (p4
.x
-p3
.x
)*(p1
.y
-p3
.y
) - (p4
.y
-p3
.y
)*(p1
.x
-p3
.x
)
253 ub_num
= (p2
.x
-p1
.x
)*(p1
.y
-p3
.y
) - (p2
.y
-p1
.y
)*(p1
.x
-p3
.x
)
256 if abs(ua_num
- ub_num
) == 0 and \
258 return max(p1
, p2
, key
=lambda v
: v
.norma())
272 def rango_oscurecido(inicio
, fin
, src
, centro
, radio
):
274 Devuelve el rango (en distancia de 0 a ||$inicio-$fin||)
275 que se encuentra a la sombra de una columna de radio $radio,
276 ubicada en $centro e iluminada por una fuente de luz puntual en $src.
279 bisectriz
= centro
- src
280 p
= math
.asin(radio
/bisectriz
.norma())
281 alpha
= bisectriz
.angulo()
283 d1
= Vector(math
.cos(alpha
+ p
), math
.sin(alpha
+ p
))
284 d2
= Vector(math
.cos(alpha
- p
), math
.sin(alpha
- p
))
286 p1
= intersect_lines((inicio
, fin
), (src
, src
+ d1
))
287 p2
= intersect_lines((inicio
, fin
), (src
, src
+ d2
))
289 unidad
= (fin
- inicio
).norma()
291 # Se sabe que los puntos de interseccion estan ordenados correctamente
292 # porque el angulo de diferencia entre las semirectas es menor a 180,
293 # entonces no es necesario chequearlo.
305 res_bounded
= interseccion_intervalos(res
, (0.0, 1.0))
306 p
= (res_bounded
[0] * unidad
, res_bounded
[1] * unidad
)
310 def interseccion_intervalos(i1
, i2
):
311 '''Devuelve los extremos del intervalo interseccion entre i1 y i2.
313 >>> interseccion_intervalos((1,3),(2,8))
315 >>> interseccion_intervalos((1,2), (5,6))
317 >>> interseccion_intervalos((-inf, 8), (6, inf))
324 d
= (max(p1
, p3
), min(p2
, p4
))
331 def reduce_interseccion(l
):
334 cand
= interseccion_intervalos(cand
, e
)
339 '''Devuelve la union de la lista de intervalos l
341 >>> reduce_union([(1,3), (2,5)])
343 >>> reduce_union([(1,3), (4, 6)])
345 >>> reduce_union([(1,3), (4, 6), (5, 6)])
347 >>> reduce_union([(1,3), (4, 6), (5, 7)])
352 ordenados
.sort(key
=lambda x
: x
[0])
359 for i
in range(1, len(l
)):
361 if otro
[0] > cand
[1] and otro
[0] != otro
[1]:
365 if otro
[1] > cand
[1]:
366 cand
= (cand
[0], otro
[1])
372 # Asume que los intervalos estan ordenados por punto de comienzo
373 def invertir_rango(ints
, fin
):
375 Invierte el conjunto de segmentos (minimo y ordenado) para
376 tomar su complemento.
378 >>> invertir_rango([(3,4)], 8)
380 >>> invertir_rango([(3,4),(6,8)], 8)
382 >>> invertir_rango([(0,2), (5,6)], 10)
391 invertido
.append((0, ints
[0][0]))
393 for i
in range(1, len(ints
)):
394 invertido
.append((ints
[i
-1][1], ints
[i
][0]))
396 if ints
[-1][1] < fin
:
397 invertido
.append((ints
[-1][1], fin
))
405 def resolver(lamparas
, columnas
, max_x
, max_y
):
407 paredes
= ((Vector(0,0), Vector(0,max_y
)),
408 (Vector(0, max_y
), Vector(max_x
, max_y
)),
409 (Vector(max_x
, max_y
), Vector(max_x
, 0)),
410 (Vector(max_x
, 0), Vector(0,0)))
419 r
= rango_oscurecido(p
[0], p
[1], l
, c
[0], c
[1])
420 zonas_tapadas
.append(r
)
421 zona_iluminada
= invertir_rango(reduce_union(zonas_tapadas
), (p
[1]-p
[0]).norma())
422 luces_pared
+= zona_iluminada
423 luces_pared
= reduce_union(luces_pared
)
425 s
= sum(map(lambda x
: x
[1] - x
[0], luces_pared
))
436 nl
, nc
, max_x
, max_y
= map(int, sys
.stdin
.readline().strip().split())
438 if nl
== nc
== max_x
== max_y
== 0:
443 lampara
= Vector(*(map(float, sys
.stdin
.readline().strip().split())))
444 lamparas
.append(lampara
)
448 x
, y
, r
= map(float, sys
.stdin
.readline().strip().split())
449 columnas
.append((Vector(x
,y
), r
))
451 resolver(lamparas
, columnas
, max_x
, max_y
)
455 if __name__
== '__main__':